home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 23 / AACD 23.iso / AACD / Programming / tek / doc / manuals / tasks3 < prev   
Encoding:
Text File  |  2001-05-23  |  5.0 KB  |  191 lines

  1.  
  2. TEKLIB MULTITASKING, USING SIGNALS
  3. ------------------------------------------------------------
  4.  
  5. let's get back to the fractal example from the last chapter. we came
  6. across that polling sucks, and this time we are going to synchronize
  7. the display properly, using signals.
  8.  
  9. we learned in the first chapter that signals are just signal bits in a
  10. task's context, with a reserved meaning. the only signal we used so
  11. far was TTASK_SIG_ABORT, a signal bit with the predefined meaning
  12. "abort this task". we used it for telling a child context to go away.
  13. the particular meaning of a signal (including TTASK_SIG_ABORT) depends
  14. on how you react to it, of course. TTASK_SIG_ABORT is just a proposal.
  15.  
  16.  
  17. so the next step would be to define what we want to synchronize on,
  18. and to define a signal for it. let's start with a signal to wakeup the
  19. parent task. this is how we reserve and deal with a custom signal in
  20. the parent context:
  21.  
  22.  
  23.     TUINT sig_parent;
  24.  
  25.     sig_parent = TAllocSignal(parenttask, 0);
  26.  
  27.     if (sig_parent)
  28.     {
  29.         /* okay, we can use the signal here */
  30.         
  31.         ..
  32.         
  33.         TFreeSignal(parenttask, sig_parent);
  34.     }
  35.     else
  36.     {
  37.         /* no more free signals */
  38.     }
  39.  
  40.  
  41. the 0 argument tells TAllocSignal() that we're happy with any free
  42. single signal. alternatively, we can allocate a preferred signal this
  43. way:
  44.  
  45.  
  46.     #define SIG_PARENT        0x80000000
  47.     
  48.     if (TAllocSignal(parenttask, SIG_PARENT))
  49.     {
  50.         /* okay, we can use the signal here */
  51.         
  52.         ..
  53.         
  54.         TFreeSignal(parenttask, SIG_PARENT);
  55.     }
  56.     else
  57.     {
  58.         /* signal 0x80000000 was not available */
  59.     }
  60.  
  61.  
  62. note that the second example may be a bit more risky. if 0x80000000
  63. was already in use for any reason, TAllocSignal() will fail. the first
  64. example will try to allocate any from a task's pool of free signals,
  65. and is therefore less likely to fail. we'll stick to the first
  66. example, and put everything together:
  67.  
  68.     
  69. struct fractaldata
  70. {
  71.     TFLOAT x1, y1, x2, y2;
  72.     TINT iterations;
  73.     TAPTR destbuffer;
  74.     TINT width, height;
  75.  
  76.     TINT line_rendered;
  77.     TBOOL finished;
  78.  
  79.     TAPTR sig_task;
  80.     TUINT sig_parent;
  81. };
  82.  
  83. void main(void)
  84. {
  85.     ..
  86.  
  87.     userdata.finished = TFALSE;    
  88.     userdata.line_rendered = 0;
  89.  
  90.     userdata.sig_task = basetask;
  91.     userdata.sig_parent = TAllocSignal(basetask, 0);
  92.  
  93.     if (userdata.sig_parent)
  94.     {
  95.         TTAGITEM tasktags[2];
  96.  
  97.         TInitTags(tasktags);
  98.         TAddTag(tasktags, TTask_UserData, &userdata);
  99.  
  100.         fractaltask = TCreateTask(basetask, fractalfunc, tasktags);
  101.         if (fractaltask)
  102.         {
  103.             TINT lastline = 0;
  104.             
  105.             do
  106.             {
  107.                 TWait(basetask, userdata.sig_parent);
  108.  
  109.                 if (userdata.line_rendered != lastline)
  110.                 {
  111.                     lastline = userdata.line_rendered;
  112.                     drawline(screen, gfxbuffer, lastline - 1);
  113.                 }
  114.  
  115.             } while (!userdata.finished);
  116.     
  117.             TDestroy(fractaltask);
  118.         }
  119.  
  120.         TFreeSignal(userdata.sig_parent);
  121.     }
  122.     ..
  123. }
  124.  
  125. TVOID fractalfunc(TAPTR task)
  126. {
  127.     struct fractaldata *userdata = TTaskGetData(task);
  128.  
  129.     while (userdata->height-- > 0)
  130.     {
  131.         /* calculate a new line here */
  132.     
  133.         userdata->line_rendered++;
  134.         TTaskSignal(userdata->sig_task, userdata->sig_parent);
  135.     }
  136.     
  137.     userdata->finished = TTRUE;
  138.     TTaskSignal(userdata->sig_task, userdata->sig_parent);
  139. }
  140.  
  141.  
  142. whenever the fractal task completed a line, it submits the parent
  143. sig_parent. this causes TWait() to return, the parent has the
  144. opportunity to check the shared data fields, and to display a new line
  145. if appropriate.
  146.  
  147. this example is still far from being perfect, and it demonstrates some
  148. not-so-obvious race conditions. it's neither likely to occur nor will
  149. it be harmful. but try to imagine what happens if, for some reason,
  150. the operating system decides to serve the child task for that long
  151. that it may have rendered more than one line before the task scheduler
  152. switches back to the parent task.
  153.  
  154. in that case, when the flow of execution is finally getting back to
  155. the parent task, the child task may have increased line_rendered and
  156. submitted sig_parent twice in the meantime. but signals are not
  157. getting queued; either a signal bit is clear, or it is set. what will
  158. happen? the parent wakes up and finds line_rendered in a modified
  159. state. it will draw the last line to the display, but it will never
  160. recognize that it forgot to draw the line before. there will be one
  161. line missing on the screen!
  162.  
  163. of course, there is an easy solution to that problem. we might modify
  164. the drawline() function to draw multiple lines at once, and call it as
  165. follows:
  166.  
  167.     TUINT newline;
  168.  
  169.     TWait(basetask, userdata.sig_parent);
  170.  
  171.     newline = userdata.line_rendered;
  172.     if (newline != lastline)
  173.     {
  174.         drawline(screen, gfxbuffer, lastline, newline - lastline);
  175.         lastline = newline;
  176.     }
  177.  
  178. but that's not all. things are getting even worse. what if the parent
  179. task woke up and started drawing the penultimate line to the screen,
  180. and while it's at that, the last line is being finished by the child
  181. task? the parent task would stumble over the while() condition and
  182. exit the loop, and the last line would be forgotten on the screen.
  183.  
  184. this is all feeling shaky. you can see that, while we're having two
  185. tasks accessing it simultaneously, the userdata structure may be
  186. getting inconsistent at any time, and a signal alone may not be a
  187. sufficient means to handle it.
  188.  
  189. to be continued...
  190.  
  191.